﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using common;

namespace Utem.Server
{
    public partial class FormControl : Form
    {
        private readonly ServerConnectionInfo _serverConnectionInfo = FileUtils.GetConnectionInfo();
        private List<PlayerInfo> _playerInfo = new List<PlayerInfo>();
        private readonly List<TextBox> _ipList = new List<TextBox>();
        private readonly List<TextBox> _nameList = new List<TextBox>();
        private readonly List<TextBox> _markList = new List<TextBox>();
        private List<QuestionManagement> _questionManagements = new List<QuestionManagement>();
        private List<int> _marks = new List<int>();

        private void LoadData()
        {
            #region load to list
            // Load to Ip list.
            _ipList.Add(txtIp1);
            _ipList.Add(txtIp2);
            _ipList.Add(txtIp3);
            _ipList.Add(txtIp4);
            _ipList.Add(txtIp5);
            _ipList.Add(txtIp6);

            // Load to Name list.
            _nameList.Add(txtPlayerName1);
            _nameList.Add(txtPlayerName2);
            _nameList.Add(txtPlayerName3);
            _nameList.Add(txtPlayerName4);
            _nameList.Add(txtPlayerName5);
            _nameList.Add(txtPlayerName6);

            // Load to Mark list.
            _markList.Add(txtMark1);
            _markList.Add(txtMark2);
            _markList.Add(txtMark3);
            _markList.Add(txtMark4);
            _markList.Add(txtMark5);
            _markList.Add(txtMark6);
            #endregion

        }

        private void FillPlayerInfo()
        {
            for (var i = 0; i < _playerInfo.Count; i++)
            {
                _ipList[i].Text = _playerInfo[i].Ip + @":" + _playerInfo[i].Port;
                _nameList[i].Text = _playerInfo[i].Name;
                _markList[i].Text = _playerInfo[i].Mark.ToString(CultureInfo.InvariantCulture);
            }
        }

        private static void ResetAllFields()
        {

        }

        private void ActionController(string action, string clientIp)
        {
            // Get 3 first characters.
            var temp = action.Substring(0, 3);
            string[] strs = null;
            switch (temp)
            {
                case "201":
                    strs = action.Split(':');
                    WriteToLog(string.Format("{0}", strs[1]));
                    break;
                case "205":
                    strs = action.Split(':');
                    WriteToLog(string.Format("{0}: {1}", clientIp, strs[1]));
                    var id = int.Parse(strs[2]);
                    Mark(id, strs[1]);
                    break;
                case "310":
                    ShowHideBonusAction(false);
                    btnStart.Text = @"Start";
                    strs = action.Split(':');
                    WriteToLog(string.Format("{0} ({1}) finish.", strs[1], clientIp));
                    break;
                case "404":
                    strs = action.Split(':');
                    WriteToLog(string.Format("{0} ({1}) throws exception: {2}", strs[1], clientIp, strs[2]));
                    break;
                default:
                    WriteToLog(action);
                    break;
            }

        }

        public void Mark(int id, string strSelected)
        {
            var selected = 0;
            switch (strSelected)
            {
                case "B":
                    selected = 1;
                    break;
                case "C":
                    selected = 2;
                    break;
                case "D":
                    selected = 3;
                    break;
            }
            if (selected == _questionManagements[listBoxQuestion.SelectedIndex].Correct)
            {
                _marks[id] = 1;
            }
            else
            {
                _marks[id] = 0;
            }
        }

        public void Mark()
        {
            for (var i = 0; i < _marks.Count; i++)
            {
                try
                {
                    _markList[i].Text = string.Format("{0}", (int.Parse(_markList[i].Text) + _marks[i]));
                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message);
                }
            }
        }

        public FormControl()
        {
            InitializeComponent();
        }

        private void FormControl_Load(object sender, EventArgs e)
        {
            var ts = new ThreadStart(StartListen);
            var th = new Thread(ts);
            th.Start();
            ResetAllFields();
            LoadData();
            WriteToLog("Application start successfully...");
            ShowHideBonusAction(false);
        }

        private void StartListen()
        {
            var listener = new TcpListener(_serverConnectionInfo.Address, _serverConnectionInfo.Port);
            listener.Start();
            while (true)
            {
                var st = new StreamReader(listener.AcceptTcpClient().GetStream());
                CheckForIllegalCrossThreadCalls = false;
                var clientIp = IPAddress.Parse(((IPEndPoint)listener.LocalEndpoint).Address.ToString());
                ActionController(st.ReadToEnd(), clientIp.ToString());
                st.Close();
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            for (var i = 0; i < _ipList.Count; i++)
            {
                try
                {
                    if (string.IsNullOrEmpty(_nameList[i].Text)) continue;
                    _marks.Add(0);
                    var temp = _ipList[i].Text.Split(':');
                    var client = new TcpClient(temp[0], int.Parse(temp[1]));
                    var sw = new StreamWriter(client.GetStream());
                    sw.Write("200:{0}:{1}", _nameList[i].Text, i);
                    sw.Flush();
                    sw.Close();
                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message + "\n");
                }
            }
            txtLog.AppendText("Connect complete.\n");
        }

        private void listBoxPlayer_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (MessageBox.Show(@"Are you sure to load new data?", @"Warnning", MessageBoxButtons.YesNo,
                MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes) return;
            try
            {
                switch (listBoxPlayer.SelectedIndex)
                {
                    case 0: // Select part I
                        _playerInfo = FileUtils.GetPlayer("partI.txt");
                        FillPlayerInfo();
                        LoadQuestionFromFile(@"data\QuestionPartI.txt");
                        break;
                    case 1: // Select part II
                        _playerInfo = FileUtils.GetPlayer("partII.txt");
                        FillPlayerInfo();
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        public void LoadQuestionFromFile(string filename)
        {
            listBoxQuestion.Items.Clear();
            _questionManagements = FileUtils.LoadQuestionFromFile(filename).ToList();
            foreach (var question in _questionManagements)
            {
                listBoxQuestion.Items.Add(question.Question);
            }

        }
        private void FormControl_KeyDown(object sender, KeyEventArgs e)
        {
        }

        private void WriteToLog(string message)
        {
            txtLog.AppendText(message + "\n");
            txtLog.SelectionStart = txtLog.TextLength;

            //// InvokeRequired required compares the thread ID of the
            //// calling thread to the thread ID of the creating thread.
            //// If these threads are different, it returns true.
            //if (this.txtLog.InvokeRequired)
            //{
            //    var d = new SetTextCallback(WriteToLog);
            //    this.Invoke(d, new object[] { message });
            //}
            //else
            //{
            //    this.txtLog.Text = message;
            //    txtLog.SelectionStart = txtLog.TextLength;
            //}

        }

        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.txtLog.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.txtLog.Text = text;
            }
        }

        private void txtLog_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.L) return;
            if (MessageBox.Show(@"Are you sure to clear log?", this.Text, MessageBoxButtons.YesNo) != DialogResult.Yes)
                return;
            txtLog.Text = "";
            txtLog.SelectionStart = txtLog.TextLength;
        }

        private void listBoxQuestion_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                txtQuestionDetails.Text = _questionManagements[listBoxQuestion.SelectedIndex].ToString();
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void btnShowQuestion_Click(object sender, EventArgs e)
        {
            const string msg = @"You are try to publish this question. When you click OK, this question will be automatic sent to all player and display form in projector. However, player will not see this question util you click start button. Please careful with this action.";
            DialogResult dialogResult =
                MessageBox.Show(msg, @"Warrning", MessageBoxButtons.OKCancel);
            if (dialogResult != DialogResult.OK) return;
            for (var i = 0; i < _ipList.Count; i++)
            {
                var isSuccess = true;
                try
                {
                    if (string.IsNullOrEmpty(_nameList[i].Text)) continue;
                    var temp = _ipList[i].Text.Split(':');
                    SendMessage(temp[0], int.Parse(temp[1]), "300:" + _questionManagements[listBoxQuestion.SelectedIndex].EncodeQuestion());
                    SendMessage(temp[0], int.Parse(temp[1]), "304:" + (listBoxQuestion.SelectedIndex + 1));

                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message + "\n");
                    isSuccess = false;
                }
                if (isSuccess)
                {
                    WriteToLog(string.Format("Send to {0}: ", _nameList[i].Text));
                }
            }
            txtLog.AppendText("Show Question action done.\n");
        }

        private void SendMessage(string ip, int port, string message)
        {
            try
            {
                var client = new TcpClient(ip, port);
                var sw = new StreamWriter(client.GetStream());
                sw.Write(message);
                sw.Flush();
                sw.Close();
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void SendMessage(string message)
        {
            for (var i = 0; i < _ipList.Count; i++)
            {
                try
                {
                    if (string.IsNullOrEmpty(_nameList[i].Text)) continue;
                    var temp = _ipList[i].Text.Split(':');
                    SendMessage(temp[0], int.Parse(temp[1]), message);

                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message);
                }
            }
        }

        private static void RaiseErrorMessage()
        {
            MessageBox.Show(@"Connect error!!! Please contact administrator, NOW!!!", @"ERROR",
                   MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Text.Equals("Start"))
            {
                btnStart.Text = @"Stop";
                for (int i = 0; i < _marks.Count; i++)
                {
                    _marks[i] = 0;
                }
                ShowHideBonusAction(true);
                SendMessage(@"301:" + txtMaxTime.Text);
            }
            else if (btnStart.Text.Equals("Stop"))
            {
                btnStart.Text = @"Start";
                SendMessage(@"302:Abort");
                ShowHideBonusAction(false);
            }
        }

        private void ShowHideBonusAction(bool isShow)
        {
            txtBonusTime.Visible = isShow;
            btnBonusTime.Visible = isShow;
        }

        private void btnBonusTime_Click(object sender, EventArgs e)
        {
            SendMessage(string.Format("303:{0}", txtBonusTime.Text));
        }

        private void btnReset_Click(object sender, EventArgs e)
        {

        }
    }
}
